"""""""""""""""""""""""""""""""""""
"""""""""""""""""""""""""""""""""""
""" GameController.py           """
""" Author: Ryan Walters        """
""" Date: 9-22-08               """
""" Revision 2                  """
"""                             """
""" This class is responsible   """
""" for handling input from the """
""" game view while translating """
""" and processing it.  It      """
""" handles getting input from  """
""" users, storing data, and    """
""" taking care of logging      """
"""""""""""""""""""""""""""""""""""
"""""""""""""""""""""""""""""""""""

# imports
# need to get the Game, Logging, the UI, and Strategy
from Game import Game
from MakerPlayer import MakerPlayer
from BreakerPlayer import BreakerPlayer
from Logging import Logging
from GameView import *
from MakerMove import MakerMove
from BreakerMove import BreakerMove
from Tkinter import Menu
from WinFrame import WinFrame
import sys

class GameController(object):
   
    # Things to handle include the following
    # A constructor, created when the App finishes initialization
    def __init__(self):
         # The following specify which variables are held in the gameController
        # A variable for the strategy of the codemaker
        self.Code_Maker_Player = None
        # A variable for the strategy of the codebreaker
        self.Code_Breaker_Player = None
        # A variable indicating that the GameController is initialized
        self.Build_Finished = False
        # A variable containing the current game
        self.game = None
        # An input frame for getting data for a new game
        self.newGameFrame = None
        # A menu bar object
        self.menu = None
        # A variable representing the GUI
        self.GUI = InitializeGameView(self)
        # A variable representing the logger
        self.Log = None

        self.displayNewGameInput()
        
        self.GUI.setMenu(self.get_menu())

        self.GUI.Game_Run_Loop()
        
    #Builds the frame that handles getting data for new games from the user.
    #Also builds a closure to handle when the "New Game" button is pressed
    def displayNewGameInput(self):
        if self.newGameFrame == None:
            def handleNewGameFinished(waitTime,maker,breaker,logPath):
                """docstring for handleNewGame"""
                self.newGame(waitTime,maker,breaker,logPath)

            self.newGameFrame = NewGameFrame(self.GUI.getRootInputView(), handleNewGameFinished, width=300, height=100)

        self.GUI.set_input_view(self.newGameFrame)
    
    #Builds and returns the menu bar object
    def get_menu(self):
        if self.menu == None:
            self.menu = Menu(self.GUI.getRoot())

            # create a pulldown menu, and add it to the menu bar
            gamemenu = Menu(self.menu, tearoff=0)
            gamemenu.add_command(label="New game", command=self.displayNewGameInput)
            gamemenu.add_command(label="Undo", command=self.undoMove)
            gamemenu.add_command(label="Switch Player", command=self.toggleBreakerPlayer)
            gamemenu.add_command(label="Toggle Logging", command=self.toggleLogging)
            gamemenu.add_command(label="Increase Comp Speed", command=self.inc_speed)
            gamemenu.add_command(label="Decrease Comp Speed", command=self.red_speed)
            gamemenu.add_separator()
            gamemenu.add_command(label="Exit", command=self.close)
            self.menu.add_cascade(label="Game", menu=gamemenu)
        return self.menu
    
    # A function to be invoked when creating a new game
    # wait time = the amount of time waiting before a move is submitted.  0 for human.
    # maker = int representing player type.  0 for Human, 1 for Computer
    # breaker = int representing player type.  0 for Human, 1 for Computer
    # log_Path = A string representing the path for logging.  None by default,
    # indicating nothing is turned on.  
    def newGame(self, waitTime, maker, breaker, log_Path):
        #1: Make a new game object and set it to the game object
        self.game = Game()
        self.GUI.getListView().reset_rows()
        self.game.register_observer(self.GUI.getListView())
        if waitTime is not None and waitTime > 30:
            waitTime = 30
        #2: Get strategy information and then set the players as appropriate
        self.Code_Maker_Player = MakerPlayer(maker)
        self.Code_Breaker_Player = BreakerPlayer(breaker, waitTime)
        self.BreakerPlayerType = breaker
        self.waitingTime = waitTime
        #3: Handle logging data prompt
        if log_Path is not None and log_Path is not "":
            self.Log = Logging(self.game, log_Path)
        self.next_move() 
            
        
    # A function to handle the toggling of logging
    def toggleLogging(self, log_Path):
        if self.Log is not None:
            self.Log.close()
            self.Log = None
        else:
            self.Log = Logging(self.game, log_Path)

    # Alternatively, have a method just to set logging state
    def setLogging(self, logSwitch, log_Path):
        if logSwitch is False:
            self.Log.close()
            self.Log = None
        elif log_Path is not None and log_Path is not "":
            self.Log = Logging(self.game, log_Path)
    
    # A function to handle adding a move to the move stack. Following this,
    # the game will continue.
    # move = the move that needs to be made.  
    def sendMove(self, move):
        moveItem = None
        if self.game.get_turn() is self.game.BREAKER_TURN:
            moveItem = BreakerMove(move)
        else:
            moveItem = MakerMove(move)
        self.game.make_move(moveItem)
        self.next_move()

    # A function to handle undoing a move
    def undoMove(self):
        if self.game.get_turn() is self.game.BREAKER_TURN and self.game.get_last_move() is not None:
            self.game.undo_last_move()
            self.game.undo_last_move()
            self.next_move()
        elif self.game.get_last_move() is not None:
            self.game.undo_last_move()
            self.next_move()
        else:
            pass
    # A function that changes what type of player the breaker is
    def toggleBreakerPlayer(self):
        if self.BreakerPlayerType is BreakerPlayer.HUMAN:
            self.setBreakerPlayer(BreakerPlayer.RANDOM_COMPUTER, self.waitingTime)
            self.BreakerPlayerType = BreakerPlayer.RANDOM_COMPUTER
            if self.game.get_turn() is self.game.BREAKER_TURN:
                self.next_move()
        else:
            self.setBreakerPlayer(BreakerPlayer.HUMAN, 0)
            self.BreakerPlayerType = BreakerPlayer.HUMAN
                                 
    # A function for setting a Codemaker strategy
    def setMakerPlayer(self, player):
        self.Code_Maker_Player = MakerPlayer(player)
        
    # A function for setting a Codebreaker strategy
    def setBreakerPlayer(self, player, waitTime):
        self.Code_Breaker_Player = BreakerPlayer(player, waitTime)
        
    # A function for starting a move from the Codebreaker strategy
    def getBreakerMove(self):
        functor = self.sendMove
        self.Code_Breaker_Player.getMove(functor, self.GUI)

    # A function for starting a move from the Codemaker strategy
    def getMakerResponse(self):
        functor = self.sendMove
        self.Code_Maker_Player.getMove(functor, self.GUI)
        
    # A function for closing the game
    def close(self):
        if self.Log is not None:
            self.Log.close()
        sys.exit(0)

    # A function to set the strategy wait time
    def setWaitTime(self, time):
        if time is not None and time > 30:
            time = 30
        elif time is None:
            time = 0
        self.waitingTime = time
        self.Code_Breaker_Player.setWaitTime(time)

    # A function to actually drive the game forward
    def next_move(self):
        winner = self.game.get_winner()
        if winner is self.game.NO_WIN:
            if self.game.get_turn() is self.game.BREAKER_TURN:
                #doesn't actually return anything, just gets the move process
                #started
                self.getBreakerMove()
            else:
                #same for the maker.  Always going to be human for v1.
                self.getMakerResponse()
                
        else:
            if self.Log is not None:
                self.Log.write_win( self.game.get_winner() )
            self.winningFrame = WinFrame(self.GUI.getRootInputView(), self.who_wins, width=300, height=100)
            self.GUI.set_input_view(self.winningFrame)
            self.winningFrame.update()

    # last minute function additions.
    # Increases speed of computer
    def inc_speed(self):
        if self.waitingTime > 0:
            self.waitingTime -= 1
            self.Code_Breaker_Player.setWaitTime(self.waitingTime)

    # Then, the opposite
    # Increases speed of computer
    def red_speed(self):
        if self.waitingTime < 30:
            self.waitingTime += 1
            self.Code_Breaker_Player.setWaitTime(self.waitingTime)

    # For the winning function
    def who_wins(self):
        return self.game.get_winner()
            
#run program
if __name__ == '__main__':
	gc = GameController()
